package top.as.sean.biz.system.impl;

import cn.dev33.satoken.stp.SaLoginConfig;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import top.as.sean.biz.system.IDept;
import top.as.sean.biz.system.IMenu;
import top.as.sean.biz.system.IUser;
import top.as.sean.biz.system.IUserPost;
import top.as.sean.biz.system.IUserRole;
import annotation.Dict;
import top.as.sean.common.constant.Constant;
import top.as.sean.common.exception.GlobalException;
import top.as.sean.common.id.SnowflakeId;
import top.as.sean.common.redis.RedisUtil;
import top.as.sean.common.response.RespStatus;
import top.as.sean.common.rsa.RsaUtils;
import top.as.sean.common.tree.TreeUtil;
import top.as.sean.dao.dto.system.AuthUserDto;
import top.as.sean.dao.dto.system.LoginDto;
import top.as.sean.dao.dto.system.UserAdd;
import top.as.sean.dao.dto.system.UserEdit;
import top.as.sean.dao.dto.system.UserPage;
import top.as.sean.dao.entity.pojo.system.Dept;
import top.as.sean.dao.entity.pojo.system.Menu;
import top.as.sean.dao.entity.pojo.system.User;
import top.as.sean.dao.mapper.system.UserMapper;
import top.as.sean.dao.vo.system.AuthUserVo;
import top.as.sean.dao.vo.system.MenuVo;
import top.as.sean.dao.vo.system.Meta;
import top.as.sean.dao.vo.system.UserInfo;
import top.as.sean.dao.vo.system.UserPageVo;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 用户
 *
 * @author: ArchieSean
 * @create: 2022/11/7
 */
@Service
@Slf4j
@AllArgsConstructor
public class UserImpl extends ServiceImpl<UserMapper, User> implements IUser {

    private final IUserPost iUserPost;

    private final IUserRole iUserRole;

    private final IDept iDept;

    private final RsaUtils rsaUtils;

    private final IMenu iMenu;

    /**
     * 退出登录
     */
    @Override
    public void lagoutUser() {
        //获取会话id
        Long loginId = Long.valueOf(StpUtil.getLoginId().toString());
        //根据会话id，会话注销，自动会删除session
        StpUtil.logout(loginId);
    }

    /**
     * 获取动态路由
     *
     * @return list
     */
    @Override
    public List<MenuVo> getRouter() {
        Long userId = Long.valueOf(StpUtil.getLoginId().toString());
        if (ObjUtil.isEmpty(userId)) {
            throw new GlobalException(RespStatus.USER_NOT_FOUNT);
        }
        //超级管理员，直接查所有动态路由
        List<Menu> menus;
        if (userId.equals(BigDecimal.ZERO.longValue())) {
            menus = iMenu.queryMenu();
        } else {
            menus = iUserRole.queryMenuByUserId(userId);
        }
        //转set，去除重复数据，并排序
        Set<Menu> set = new LinkedHashSet<>(menus);
        List<MenuVo> list = Lists.newArrayList();
        set.forEach(menu -> {
            MenuVo menuVo = new MenuVo();
            BeanUtils.copyProperties(menu, menuVo);
            menuVo.setHideMenu(Boolean.TRUE);
            if (Constant.MenuVisible.SHOW.getCode().equals(menu.getVisible())) {
                //菜单显示
                menuVo.setHideMenu(Boolean.FALSE);
            }
            menuVo.setId(menu.getMenuId());
            menuVo.setParentId(menu.getParentId());
            Meta meta = new Meta();
            meta.setTitle(menu.getMenuName());
            meta.setIcon(menu.getIcon());
            menuVo.setMeta(meta);
            list.add(menuVo);
        });
        List<MenuVo> vos = TreeUtil.build(list, BigDecimal.ZERO.longValue(), Comparator.comparing(MenuVo::getOrderNum));
        dealWithData(vos);
        return vos;
    }

    /**
     * 去除部分无用数据
     *
     * @param list list
     */
    private void dealWithData(List<MenuVo> list) {
        list.forEach(item -> {
            item.setId(null);
            item.setOrderNum(null);
            item.setParentId(null);
            List children = item.getChildren();
            if (!CollectionUtils.isEmpty(children)) {
                dealWithData(children);
            }
        });
    }

    /**
     * 分页查询用户信息
     *
     * @param dto 入参
     * @return page
     */
    @Dict
    @Override
    public Page<UserPageVo> userPage(UserPage dto) {
        List<Dept> dept = iDept.findByIds(null);
        //获取部门以及子部门数据
        List<Dept> depths = Lists.newArrayList();
        if (ObjUtil.isNotEmpty(dto.getDeptId())) {
            getDeptData(dept, dto.getDeptId(), depths);
        }
        List<Long> deptIds = depths.parallelStream().map(Dept::getId).collect(Collectors.toList());
        Page<User> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        this.lambdaQuery().in(!CollectionUtils.isEmpty(deptIds), User::getDeptId, deptIds).like(StringUtils.hasLength(dto.getUsername()), User::getUsername, dto.getUsername()).like(StringUtils.hasLength(dto.getPhoneNumber()), User::getPhoneNumber, dto.getPhoneNumber()).eq(StringUtils.hasLength(dto.getStatus()), User::getStatus, dto.getStatus()).page(page);
        Page<UserPageVo> voPage = (Page<UserPageVo>) page.convert(user -> {
            UserPageVo pageVo = new UserPageVo();
            BeanUtils.copyProperties(user, pageVo);
            pageVo.setStatusDict(user.getStatus());
            return pageVo;
        });
        List<UserPageVo> users = voPage.getRecords();
        if (CollectionUtils.isEmpty(users)) {
            return voPage;
        }
        Map<Long, String> deptMap = dept.parallelStream().collect(Collectors.toMap(Dept::getId, Dept::getDeptName));
        return (Page<UserPageVo>) voPage.convert(vo -> {
            //获取部门id
            Long deptId = vo.getDeptId();
            if (ObjUtil.isNotEmpty(deptId) && deptMap.containsKey(deptId)) {
                vo.setDeptName(deptMap.get(deptId));
            }
            return vo;
        });
    }

    /**
     * 帝国获取部门数据
     *
     * @param list   list
     * @param deptId 部门id
     */
    private void getDeptData(List<Dept> list, Long deptId, List<Dept> result) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        Map<Long, List<Dept>> map = list.stream().collect(Collectors.groupingBy(Dept::getParentId));
        List<Dept> deptList = list.parallelStream().filter(dept -> deptId.equals(dept.getId())).collect(Collectors.toList());
        getChildren(map, deptList, result);
    }

    /**
     * @param map    map
     * @param list   部门子集
     * @param result result
     */
    private void getChildren(Map<Long, List<Dept>> map, List<Dept> list, List<Dept> result) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        result.addAll(list);
        list.forEach(dept -> {
            Long id = dept.getId();
            if (map.containsKey(id)) {
                List<Dept> depts = map.get(id);
                getChildren(map, depts, result);
            }
        });
    }

    /**
     * 新增用户信息
     *
     * @param dto 新增用户
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean addUser(UserAdd dto) {
        //校验密码合法性
        rsaUtils.checkLegal(dto.getPassword());
        if (validateUsername(dto.getUsername())) {
            throw new GlobalException(RespStatus.USER_EXIST_USERNAME);
        }
        //新增用户
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        user.setUserId(SnowflakeId.createId());
        save(user);
        //获取岗位ids
        if (!CollectionUtils.isEmpty(dto.getPostIds())) {
            iUserPost.saveBatchIds(dto.getPostIds(), user.getUserId());
        }
        //角色ids
        if (!CollectionUtils.isEmpty(dto.getRoleIds())) {
            iUserRole.saveBatchIds(dto.getRoleIds(), user.getUserId());
        }
        return Boolean.TRUE;
    }

    /**
     * 校验账号是否存在
     *
     * @param username 账号
     * @return boolean
     */
    private Boolean validateUsername(@NotNull String username) {
        return this.lambdaQuery().eq(User::getUsername, username).exists();
    }

    /**
     * 用户信息
     *
     * @param dto 入参
     * @return userInfo
     */
    @Override
    public String login(LoginDto dto) {
        //校验验证码
        String key = dto.getKey();
        if (!RedisUtil.isKeyExist(key)) {
            throw new GlobalException(RespStatus.CODE_TIME_OUT);
        }
        String codeStr = RedisUtil.getObject(key).toString();
        if (!dto.getCode().equalsIgnoreCase(codeStr)) {
            throw new GlobalException(RespStatus.CODE_NOT_VALID);
        }

        //根据账号查询用户信息
        User user = this.lambdaQuery().eq(User::getUsername, dto.getUsername()).eq(User::getStatus, Constant.CommonStatus.ENABLE.getCode()).one();
        if (ObjectUtils.isEmpty(user)) {
            throw new GlobalException(RespStatus.USER_NOT_FOUNT);
        }
        //账号密码正确
        if (dto.getUsername().equals(user.getUsername()) && rsaUtils.decode(dto.getPassword()).equals(rsaUtils.decode(user.getPassword()))) {
            HashMap<String, Object> map = Maps.newHashMap();
            map.put("username", user.getUsername());
            map.put("userId", user.getUserId());
            StpUtil.login(user.getUserId(), SaLoginConfig.setExtraData(map));
        }
        return StpUtil.getTokenValue();
    }

    /**
     * 获取用户信息
     *
     * @return obj
     */
    @Override
    @Dict
    public UserInfo getInfo() {
        UserInfo info = new UserInfo();
        Object userId = StpUtil.getLoginId();
        log.info("获取到的用户id----->{}", userId);
        //根据用户id获取用户信息
        User user = this.lambdaQuery().eq(User::getUserId, userId).one();
        if (ObjectUtils.isEmpty(user)) {
            throw new GlobalException(RespStatus.USER_NOT_FOUNT);
        }
        BeanUtils.copyProperties(user, info);
        info.setSexDict(user.getSex());

        Long deptId = user.getDeptId();
        if (ObjUtil.isNotEmpty(deptId)) {
            Dept dept = iDept.lambdaQuery().eq(Dept::getId, deptId).one();
            info.setDeptName(Optional.ofNullable(dept).isPresent() ? dept.getDeptName() : "");
        }
        //查询权限标识符
        Set<String> btnList = iUserRole.queryBtnList(userId,Constant.MenuType.BUTTON.getCode());
        info.setButtonList(btnList);
        //角色名称
        Set<String> roleNames = iUserRole.queryRoleName(userId);
        info.setRoleNames(roleNames);
        return info;
    }

    /**
     * 删除用户
     *
     * @param ids 用户ids
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(List<Long> ids) {
        //1.删除关联表
        iUserRole.deleteByUserId(ids);
        //2.删除用户数据
        removeBatchByIds(ids);
    }

    /**
     * @param dto 入参
     * @return boolean
     */
    @Override
    public Boolean editUser(UserEdit dto) {
        if (dto.getUserId().equals(BigDecimal.ZERO.longValue())) {
            throw new GlobalException(RespStatus.SUPER_ADMIN_NOT_EDIT);
        }
        //获取岗位ids
        List<Long> postIds = dto.getPostIds();
        //角色ids
        List<Long> roleIds = dto.getRoleIds();

        if (!CollectionUtils.isEmpty(postIds)) {
            iUserPost.saveBatchIds(postIds, dto.getUserId());
        }

        if (!CollectionUtils.isEmpty(roleIds)) {
            iUserRole.saveBatchIds(roleIds, dto.getUserId());
        }

        User edit = new User();
        BeanUtils.copyProperties(dto, edit);
        return updateById(edit);
    }

    /**
     * 查询分配用户列表
     *
     * @param dto 入参
     * @return page
     */
    @Dict
    @Override
    public Page<AuthUserVo> getAuthList(AuthUserDto dto, Boolean flag) {
        Page<AuthUserVo> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        List<Long> userIds = iUserRole.queryByRoleId(dto.getRoleId());
        if (CollectionUtils.isEmpty(userIds)) {
            return page;
        }
        Page<User> userPage = lambdaQuery()
                //正向查询，当前角色下的用户
                .in(!flag, User::getUserId, userIds)
                //反向查询，当前角色下没有的用户
                .notIn(flag, User::getUserId, userIds).like(StringUtils.hasLength(dto.getUsername()), User::getUsername, dto.getUsername()).like(StringUtils.hasLength(dto.getPhoneNumber()), User::getPhoneNumber, dto.getPhoneNumber()).page(new Page<>(dto.getPageNum(), dto.getPageSize()));
        return (Page<AuthUserVo>) userPage.convert(user -> {
            AuthUserVo vo = new AuthUserVo();
            BeanUtils.copyProperties(user, vo);
            vo.setStatusDict(user.getStatus());
            return vo;
        });
    }

}
